﻿using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;

namespace TransportLayer.NodeLibrary
{
    public class SFMLRM : IFrameReceiver
    {
        private Dictionary<CTP, CTP> matrix;
        private Dictionary<int, CTP> map;
        private Node node;
        private Random rand;

        private int Rand { get { return rand.Next() % 100; } }

        public SFMLRM(Node node)
        {
            this.node = node;
            this.rand = new Random();
            matrix = new Dictionary<CTP, CTP>(new CTP.REComparer());
            map = new Dictionary<int, CTP>();
        }


        public SFMLRM(SFMLRM sfm)// : base((Dictionary<RoutingEntry, RoutingEntry>)routingTable, new RoutingEntry.EqualityComparer()) { }
        {
            matrix = new Dictionary<CTP, CTP>(sfm.matrix, new CTP.REComparer());
            map = new Dictionary<int, CTP>(sfm.map);

        }

 

        public void ReceiveFrame(Frame pu, int port)
        {
            CTP key = new CTP(pu.Stream, port);
            if (matrix.ContainsKey(key) && pu.Put == ProtocolUnitType.NORMAL_PACKET)
            {
                CTP forward = matrix[key];

                if (pu.Put == ProtocolUnitType.NORMAL_PACKET && node.PortsOut.Length >= forward.Port && node.PortsOut[forward.Port] != null)
                {
                    pu.Stream = forward.Stream;
                    node.PortsOut[forward.Port].Send(pu);
                    node.Log.LogMsg("Sending " + key.Stream + ":" + key.Port + ">" + forward.Stream + ":" + forward.Port + ".");
                }
                else node.Log.LogMsg("Ramka " + pu.Stream + ":" + port + "odrzucona.");

            }

            else if (pu.Put == ProtocolUnitType.CONF_PACKET)
            {
                string[] param = pu.PduMessage.Split(' ');
                CTP re = new CTP(Int32.Parse(param[0]), port);
                int connectionID = Int32.Parse(param[1]);
                if (!matrix.ContainsKey(re))
                {
                    if (map.ContainsKey(connectionID))
                        map.Remove(connectionID);
                    map.Add(connectionID, re);
                    node.Snp.Send("ok");

                }
                else node.Snp.Send("fail");
            }

        }

        public bool ContainsKey(int connectionID)
        {
            return map.ContainsKey(connectionID);

        }

        public bool ContainsKey(CTP re)
        {
            return matrix.ContainsKey(re);

        }

        public bool ContainsValue(CTP re)
        {
            return matrix.ContainsValue(re);
        }


        /*
         * to remove evry MatrixLink with this ID
         */
        public bool Remove(int connectionID)
        {
            if (map.ContainsKey(connectionID))
            {
                matrix.Remove(map[connectionID]);
                // map.Remove(connectionID);
                return map.Remove(connectionID); ;
            }
            else return false;
        }



        public bool Add(string command, string connectionID)
        {
            try
            {
                string[] param = command.Split('>');

                int conID = Int32.Parse(connectionID);
                if (param.Length == 2 && map.ContainsKey(conID))
                {
                    int portIn = Int32.Parse(param[0]);
                    int portOut = Int32.Parse(param[1]);


                    CTP entry = map[conID];
                    int stream = Rand;
                    while (map.ContainsKey(stream))
                    { stream = Rand; }
                    CTP depart = new CTP(stream, portOut);
                    if (matrix.ContainsKey(entry))
                        matrix.Remove(entry);

                    matrix.Add(entry, depart);

                    node.PortsOut[portOut].Send(this.ConfigProtocolUnit(depart.Stream, conID));


                    return true;
                }

            }
            catch (FormatException) { return false; }
            catch (ArgumentException) { return false; }
            catch (IndexOutOfRangeException) { return false; }
            catch (CTP.ArgumentsLenghtException) { return false; }

            return false;

        }

        private Frame ConfigProtocolUnit(int rand, int connectionID)
        {
            Frame pu = new Frame();
            pu.Put = ProtocolUnitType.CONF_PACKET;
            pu.PduMessage = "" + rand + " " + connectionID;

            return pu;
        }
        public void Add(CTP e1, CTP e2)
        {
            matrix.Add(e1, e2);
            map.Add(e1.Stream, e1);
        }


        public override string ToString()
        {
            string str = "";
            foreach (KeyValuePair<int, CTP> kvp in map)
                str += "[" + kvp.Key + "] " + kvp.Value.ToString() + ">" + matrix[kvp.Value].ToString() + "\n";
            return str;
        }
    }

    public class CTP : System.IEquatable<CTP>
    {
        private int port;
        public int Port { get { return port; } }

        private int stream;
        public int Stream { get { return stream; } }

        public static IEqualityComparer<CTP> Default
        { get { return (IEqualityComparer<CTP>)(new REComparer()); } }

        public CTP(int stream, int port)
        {
            this.stream = stream;
            this.port = port;

        }

        public CTP(string comand)
        {
            comand = comand.Replace(">", "");
            string[] param = comand.Split(';', ':', ',');
            if (param.Length == 2)
            {
                this.stream = Int32.Parse(param[0]);
                this.port = Int32.Parse(param[1]);
            }
            else throw new ArgumentsLenghtException();
        }

        public class REComparer : IEqualityComparer<CTP>
        {
            public bool Equals(CTP e1, CTP e2)
            {
                if (e1.Stream == e2.Stream && e1.Port == e2.Port)
                    return true;
                else
                    return false;

            }

            public int GetHashCode(CTP entry)
            {
                return entry.Port ^ entry.Stream;
            }
        }

        public override string ToString()
        {
            return this.Stream + ":" + this.Port;
        }

        public bool Equals(CTP other)
        {
            if (this.Stream == other.Stream && this.Port == other.Port)
                return true;
            else
                return false;

        }

        public class ArgumentsLenghtException : Exception
        {
            public int Id = -1;
            public ArgumentsLenghtException()
            { }
            public ArgumentsLenghtException(int id)
            {
                Id = id;
            }
        }

    }

}